#include "pch.h"

#include "..\RootRpcInterface\RootRpcInterface.h"
#include "RootRpcClient.h"

LONG MapHKeyForRpc(PHKEY key);
DWORD GetMultiStringSize(PWCHAR msz);

#ifndef NO_ROOTRPC_FILE_APIS

ROOTRPC_EXPORTED BOOL RootCopyFile(
	LPCWSTR lpExistingFileName,
	LPCWSTR lpNewFileName,
	BOOL bFailIfExists)
{
	WINERR err;
	RpcTryExcept
		err = ::RootRpcCopyFile(lpExistingFileName, lpNewFileName, bFailIfExists);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (err)
	{
		::SetLastError(err);
	}
	return !err;
}

ROOTRPC_EXPORTED HRESULT RootCopyFile2(
	LPCWSTR pwszExistingFileName,
	LPCWSTR pwszNewFileName,
	COPYFILE2_EXTENDED_PARAMETERS * pExtendedParameters)
{
	if (pExtendedParameters)
	{
		if (pExtendedParameters->pfCancel || 
			pExtendedParameters->pProgressRoutine ||
			pExtendedParameters->pvCallbackContext)
		{	// We don't support cancelation or callback at this time
			return HRESULT_FROM_WIN32(ERROR_NOT_CAPABLE);
		}
		ROOTRPC_COPYFILE2_EXTENDED_PARAMETERS p;
		p.dwSize = pExtendedParameters->dwSize;
		p.dwCopyFlags = pExtendedParameters->dwCopyFlags;
		p.pfCancel = nullptr;
		p.pProgressRoutine = 0x0;
		p.pvCallbackContext = 0x0;
		RpcTryExcept
			return ::RootRpcCopyFile2(pwszExistingFileName, pwszNewFileName, &p);
		RpcExcept(EXCEPTION_EXECUTE_HANDLER)
			return ::HRESULT_FROM_WIN32(::RpcExceptionCode());
		RpcEndExcept
	}
	return ::RootRpcCopyFile2(pwszExistingFileName, pwszNewFileName, nullptr);
}

ROOTRPC_EXPORTED HANDLE RootCreateFile(
	LPCWSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile)
{
	HANDLE retval = INVALID_HANDLE_VALUE;
	WINERR errval = 0x0;
	ROOTRPC_SECURITY_ATTRIBUTES sa;
	if (lpSecurityAttributes)
	{
		sa.nLength = lpSecurityAttributes->nLength;
		sa.lpSecurityDescriptor = reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
				lpSecurityAttributes->lpSecurityDescriptor);
		sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	}
	RpcTryExcept
		errval = ::RootRpcCreateFile(
			lpFileName,
			dwDesiredAccess,
			dwShareMode,
			lpSecurityAttributes ? &sa : nullptr,
			dwCreationDisposition,
			dwFlagsAndAttributes,
			hTemplateFile,
			&retval);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
	}
	return retval;
}

ROOTRPC_EXPORTED HANDLE RootCreateFile2(
	LPCWSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	DWORD dwCreationDisposition,
	LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams)
{
	HANDLE retval = INVALID_HANDLE_VALUE;
	WINERR errval = 0x0;
	ROOTRPC_CREATEFILE2_EXTENDED_PARAMETERS ep;
	ROOTRPC_SECURITY_ATTRIBUTES sa;
	if (pCreateExParams)
	{
		if (pCreateExParams->lpSecurityAttributes)
		{
			sa.nLength = pCreateExParams->lpSecurityAttributes->nLength;
			sa.lpSecurityDescriptor = reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
						pCreateExParams->lpSecurityAttributes->lpSecurityDescriptor);
			sa.bInheritHandle = pCreateExParams->lpSecurityAttributes->bInheritHandle;
			ep.lpSecurityAttributes = &sa;
		}
		else
		{
			ep.lpSecurityAttributes = nullptr;
		}
		ep.dwSize = pCreateExParams->dwSize;
		ep.dwFileAttributes = pCreateExParams->dwFileAttributes;
		ep.dwFileFlags = pCreateExParams->dwFileFlags;
		ep.dwSecurityQosFlags = pCreateExParams->dwSecurityQosFlags;
		ep.hTemplateFile = pCreateExParams->hTemplateFile;
	}
	RpcTryExcept
		errval = ::RootRpcCreateFile2(
			lpFileName,
			dwDesiredAccess,
			dwShareMode,
			dwCreationDisposition,
			pCreateExParams ? &ep : nullptr,
			&retval);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
	}
	return retval;
}

ROOTRPC_EXPORTED BOOL RootCreateHardLink(
	LPCWSTR lpFileName,
	LPCWSTR lpExistingFileName,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcCreateHardLink(
			lpFileName,
			lpExistingFileName,
			reinterpret_cast<ROOTRPC_LPSECURITY_ATTRIBUTES>(lpSecurityAttributes));
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
	}
	return !errval;
}

ROOTRPC_EXPORTED BOOLEAN RootCreateSymbolicLink(
	LPWSTR lpSymbolicFileName,
	LPWSTR lpTargetFileName,
	DWORD dwFlags)
{
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcCreateSymbolicLink(lpSymbolicFileName, lpTargetFileName, dwFlags);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
	}
	return (BOOLEAN)(!errval);
}

ROOTRPC_EXPORTED BOOL RootDeleteFile(
	LPCWSTR lpFileName)
{
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcDeleteFile(lpFileName);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
	}
	return !errval;
}

ROOTRPC_EXPORTED BOOL RootFindClose(
	HANDLE hFindFile)
{
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcFindClose(&hFindFile);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
	}
	return !errval;
}

ROOTRPC_EXPORTED HANDLE RootFindFirstFile(
	LPCWSTR lpFileName,
	LPWIN32_FIND_DATAW lpFindFileData)
{
	HANDLE outval = nullptr;
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcFindFirstFile(lpFileName, lpFindFileData, &outval);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
	}
	if (!outval)
	{
		// The function failed for some reason, returned INVALID_HANDLE_VALUE. (Or RPC failed...)
		// Due to the way RPC context handles work, the transmitted value had to be NULL.
		return INVALID_HANDLE_VALUE;
	}
	if (INVALID_HANDLE_VALUE == outval)
	{
		// The server function returned NULL. This might actually be a legit HANDLE.
		// However, for RPC context handle reasons, we couldn't transmit it as NULL.
		return nullptr;
	}
	return outval;
}

ROOTRPC_EXPORTED HANDLE RootFindFirstFileEx(
	LPCWSTR lpFileName,
	FINDEX_INFO_LEVELS fInfoLevelId,
	PWIN32_FIND_DATAW lpFindFileData,
	FINDEX_SEARCH_OPS fSearchOp,
	LPVOID lpSearchFilter,
	DWORD dwAdditionalFlags)
{
	HANDLE retval = nullptr;
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcFindFirstFileEx(
			lpFileName,
			fInfoLevelId,
			lpFindFileData,
			fSearchOp,
			reinterpret_cast<RPC_PTR>(lpSearchFilter),
			dwAdditionalFlags,
			(FINDHANDLE*)(&retval));
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
	}
	if (!retval)
	{
		// The function failed for some reason, returned INVALID_HANDLE_VALUE. (Or RPC failed...)
		// Due to the way RPC context handles work, the transmitted value had to be NULL.
		return INVALID_HANDLE_VALUE;
	}
	if (INVALID_HANDLE_VALUE == retval)
	{
		// The server function returned NULL. This might actually be a legit HANDLE.
		// However, for RPC context handle reasons, we couldn't transmit it as NULL.
		return nullptr;
	}
	return retval;
}

ROOTRPC_EXPORTED BOOL RootFindNextFile(
	HANDLE hFindFile,
	LPWIN32_FIND_DATAW lpFindFileData)
{
	WINERR errval;
	if (INVALID_HANDLE_VALUE == hFindFile)
	{
		// Not a real FINDHANDLE
		::SetLastError(ERROR_INVALID_HANDLE);
		return FALSE;
	}
	if (!hFindFile)
	{
		// Because of the way context handles work, spoof this value
		hFindFile = INVALID_HANDLE_VALUE;
	}
	RpcTryExcept
		errval = ::RootRpcFindNextFile((FINDHANDLE)hFindFile, lpFindFileData);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return FALSE;
	}
	return TRUE;
}

ROOTRPC_EXPORTED DWORD RootGetFileAttributes(
	LPCWSTR lpFileName)
{
	WINERR errval;
	DWORD ret = INVALID_FILE_ATTRIBUTES;
	RpcTryExcept
		errval = ::RootRpcGetFileAttributes(lpFileName, &ret);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
	}
	return ret;
}

ROOTRPC_EXPORTED BOOL RootGetFileAttributesEx(
	LPCWSTR lpFileName,
	GET_FILEEX_INFO_LEVELS fInfoLevelId,
	LPWIN32_FILE_ATTRIBUTE_DATA lpFileInformation)
{
	WINERR errval;
	if (!lpFileInformation)
	{
		return ERROR_INVALID_PARAMETER;
	}
	RpcTryExcept
		errval = ::RootRpcGetFileAttributesEx(lpFileName, fInfoLevelId, lpFileInformation);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return FALSE;
	}
	return TRUE;
}

ROOTRPC_EXPORTED BOOL RootMoveFile(
	LPCWSTR lpExistingFileName,
	LPCWSTR lpNewFileName)
{
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcMoveFile(lpExistingFileName, lpNewFileName);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return FALSE;
	}
	return TRUE;
}

ROOTRPC_EXPORTED BOOL RootMoveFileEx(
	LPCWSTR lpExistingFileName,
	LPCWSTR lpNewFileName,
	DWORD dwFlags)
{
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcMoveFileEx(lpExistingFileName, lpNewFileName, dwFlags);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return FALSE;
	}
	return TRUE;
}

ROOTRPC_EXPORTED BOOL RootReplaceFile(
	LPCWSTR lpReplacedFileName,
	LPCWSTR lpReplacementFileName,
	LPCWSTR lpBackupFileName,
	DWORD dwReplaceFlags,
	LPVOID lpExclude,
	LPVOID lpReserved)
{
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcReplaceFile(
			lpReplacedFileName,
			lpReplacementFileName,
			lpBackupFileName,
			dwReplaceFlags,
			reinterpret_cast<RPC_PTR>(lpExclude),
			reinterpret_cast<RPC_PTR>(lpReserved));
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return FALSE;
	}
	return TRUE;
}

ROOTRPC_EXPORTED DWORD RootSearchPath(
	LPCWSTR lpPath,
	LPCWSTR lpFileName,
	LPCWSTR lpExtension,
	DWORD nBufferLength,
	LPWSTR lpBuffer,
	LPWSTR * lpFilePart);

ROOTRPC_EXPORTED BOOL RootSetFileAttributes(
	LPCWSTR lpFileName,
	DWORD dwFileAttributes)
{
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcSetFileAttributes(lpFileName, dwFileAttributes);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return FALSE;
	}
	return TRUE;
}

ROOTRPC_EXPORTED BOOL RootCreateDirectory(
	LPCWSTR lpPathName,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
	WINERR errval;
	ROOTRPC_SECURITY_ATTRIBUTES sa = { 0 };
	if (lpSecurityAttributes)
	{
		sa.nLength = lpSecurityAttributes->nLength;
		sa.lpSecurityDescriptor = reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
			lpSecurityAttributes->lpSecurityDescriptor);
		sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	}
	RpcTryExcept
		errval = ::RootRpcCreateDirectory(lpPathName, nullptr);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return FALSE;
	}
	return TRUE;
}

ROOTRPC_EXPORTED BOOL RootCreateDirectoryEx(
	LPCWSTR lpTemplateDirectory,
	LPCWSTR lpNewDirectory,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
	WINERR errval;
	ROOTRPC_SECURITY_ATTRIBUTES sa = { 0 };
	if (lpSecurityAttributes)
	{
		sa.nLength = lpSecurityAttributes->nLength;
		sa.lpSecurityDescriptor = 
			reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
				lpSecurityAttributes->lpSecurityDescriptor);
		sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
	}
	RpcTryExcept
		errval = ::RootRpcCreateDirectoryEx(
			lpTemplateDirectory, 
			lpNewDirectory, 
			(lpSecurityAttributes ? &sa : nullptr));
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return FALSE;
	}
	return TRUE;
}

ROOTRPC_EXPORTED HANDLE RootFindFirstChangeNotification(
	LPCWSTR lpPathName,
	BOOL bWatchSubtree,
	DWORD dwNotifyFilter)
{
	WINERR errval;
	HANDLE outval = INVALID_HANDLE_VALUE;
	RpcTryExcept
		errval = ::RootRpcFindFirstChangeNotification(lpPathName, bWatchSubtree, dwNotifyFilter, &outval);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return INVALID_HANDLE_VALUE;
	}
	return outval;
}

ROOTRPC_EXPORTED BOOL RootRemoveDirectory(
	LPCWSTR lpPathName)
{
	WINERR errval;
	RpcTryExcept
		errval = ::RootRpcRemoveDirectory(lpPathName);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		errval = ::RpcExceptionCode();
	RpcEndExcept
	if (errval)
	{
		::SetLastError(errval);
		return FALSE;
	}
	return TRUE;
}

#endif

#ifndef NO_ROOTRPC_DRIVE_APIS

// TODO: All the DosDevice, FindVolume, and FindVMP stuff.

ROOTRPC_EXPORTED UINT RootGetDriveType(
	LPCWSTR lpRootPathName)
{
	RpcTryExcept
		return ::RootRpcGetDriveType(lpRootPathName);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		::SetLastError(::RpcExceptionCode());
		return 0x0;
	RpcEndExcept
}

// TODO: All the Volume and DosDevice stuff.

#endif

#ifndef NO_ROOTRPC_REG_APIS

// Not actually wrapping a RootRpc function, just wrapping a native one with RootRpc help.
ROOTRPC_EXPORTED LONG RootRegCopyTree(
	HKEY hKeySrc,
	LPCWSTR lpSubKey,
	HKEY hKeyDest)
{
	HKEY hk;
	LONG errval;
#if 0	// Just always open the key. Saves worrying about permissions near the roots.
	if (!lpSubKey || !wcslen(lpSubKey))
	{
		// No subkey. Just act on the source key.
		return ::RegCopyTreeW(hKeySrc, lpSubKey, hKeyDest);
	}
	// The subkey string exists and is not empty. OK, open that key
#endif
	errval = ::RootRegOpenKeyEx(hKeySrc, lpSubKey, 0x0, KEY_READ, &hk);
	if (errval)
	{
		return errval;
	}
	errval = ::RegCopyTreeW(hk, nullptr, hKeyDest);
	::RegCloseKey(hk);
	return errval;
}

ROOTRPC_EXPORTED LONG RootRegCreateKeyEx(
	HKEY hKey,
	LPCWSTR lpSubKey,
	DWORD Reserved,
	LPWSTR lpClass,
	DWORD dwOptions,
	REGSAM samDesired,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	PHKEY phkResult,
	LPDWORD lpdwDisposition)
{
	ROOTRPC_SECURITY_ATTRIBUTES sa;
	LONG status;
	if (!lpSubKey || !phkResult)
	{	// These parameters are required.
		*phkResult = nullptr;
		return ERROR_INVALID_PARAMETER;
	}
	if (lpSecurityAttributes)
	{
		sa.nLength = lpSecurityAttributes->nLength;
		sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
		sa.lpSecurityDescriptor = reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
			lpSecurityAttributes->lpSecurityDescriptor);
	}
	status = MapHKeyForRpc(&hKey);
	if (!hKey)
	{
		*phkResult = nullptr;
		return status;
	}
	RpcTryExcept
		return ::RootRpcRegCreateKeyEx(
			hKey,
			lpSubKey,
			Reserved,
			lpClass,
			dwOptions,
			samDesired,
			lpSecurityAttributes ? &sa : nullptr,
			phkResult,
			lpdwDisposition);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		*phkResult = nullptr;
		return ::RpcExceptionCode();
	RpcEndExcept
}

ROOTRPC_EXPORTED LONG RootRegDeleteKey(
	HKEY hKey,
	LPCWSTR lpSubKey)
{
	LONG status = MapHKeyForRpc(&hKey);
	if (!hKey)
	{
		return status;
	}
	RpcTryExcept
		return ::RootRpcRegDeleteKey(hKey, lpSubKey);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		return ::RpcExceptionCode();
	RpcEndExcept
}

ROOTRPC_EXPORTED LONG RootRegDeleteKeyEx(
	HKEY hKey,
	LPCWSTR lpSubKey,
	REGSAM samDesired,
	DWORD Reserved)
{
	LONG status = MapHKeyForRpc(&hKey);
	if (!hKey)
	{
		return status;
	}
	RpcTryExcept
		return ::RootRpcRegDeleteKeyEx(hKey, lpSubKey, samDesired, Reserved);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		return ::RpcExceptionCode();
	RpcEndExcept
}

// Not actually wrapping a RootRpc function, just wrapping a native one with RootRpc help.
ROOTRPC_EXPORTED LONG RootRegDeleteKeyValue(
	HKEY hKey,
	LPCWSTR lpSubKey,
	LPCWSTR lpValueName)
{
	HKEY hk;
	LONG errval = ::RootRegOpenKeyEx(hKey, lpSubKey, 0x0, KEY_SET_VALUE, &hk);
	if (errval)
	{
		return errval;
	}
	errval = ::RegDeleteKeyValueW(hk, nullptr, lpValueName);
	::RegCloseKey(hk);
	return errval;
}

ROOTRPC_EXPORTED LONG RootRegDeleteTree(
	HKEY hKey,
	LPCWSTR lpSubKey)
{
	LONG status = MapHKeyForRpc(&hKey);
	if (!hKey)
	{
		return status;
	}
	RpcTryExcept
		return ::RootRpcRegDeleteTree(hKey, lpSubKey);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		return ::RpcExceptionCode();
	RpcEndExcept
}

// Not actually wrapping a RootRpc function, just wrapping a native one with RootRpc help.
ROOTRPC_EXPORTED LONG RootRegGetValue(
	HKEY hKey,
	LPCTSTR lpSubKey,
	LPCTSTR lpValue,
	DWORD dwFlags,
	LPDWORD pdwType,
	PVOID pvData,
	LPDWORD pcbData)
{
	HKEY hk;
	LONG errval = ::RootRegOpenKeyEx(hKey, lpSubKey, 0x0, KEY_QUERY_VALUE, &hk);
	if (errval)
	{
		return errval;
	}
	errval = ::RegGetValueW(hk, nullptr, lpValue, dwFlags, pdwType, pvData, pcbData);
	::RegCloseKey(hk);
	return errval;
}

ROOTRPC_EXPORTED LONG RootRegLoadKey(
	HKEY hKey,
	LPCWSTR lpSubKey,
	LPCWSTR lpFile);

ROOTRPC_EXPORTED LONG RootRegOpenCurrentUser(
	REGSAM samDesired,
	PHKEY phkResult);

ROOTRPC_EXPORTED LONG RootRegOpenKeyEx(
	HKEY hKey,
	LPCWSTR lpSubKey,
	DWORD ulOptions,
	REGSAM samDesired,
	PHKEY phkResult)
{
	LONG status;
	if (!phkResult)
	{
		return ERROR_INVALID_PARAMETER;
	}
	status = MapHKeyForRpc(&hKey);
	if (!hKey)
	{
		*phkResult = nullptr;
		return status;
	}
	RpcTryExcept
		return ::RootRpcRegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, phkResult);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		*phkResult = nullptr;
		return ::RpcExceptionCode();
	RpcEndExcept
}

ROOTRPC_EXPORTED LONG RootRegReplaceKey(
	HKEY hKey,
	LPCWSTR lpSubKey,
	LPCWSTR lpNewFile,
	LPCWSTR lpOldFile)
{
	LONG status = MapHKeyForRpc(&hKey);
	if (!hKey)
	{
		return status;
	}
	RpcTryExcept
		return ::RootRpcRegReplaceKey(hKey, lpSubKey, lpNewFile, lpOldFile);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		return ::RpcExceptionCode();
	RpcEndExcept
}

ROOTRPC_EXPORTED LONG RootRegRestoreKey(
	HKEY hKey,
	LPCWSTR lpFile,
	DWORD dwFlags)
{
	LONG status = MapHKeyForRpc(&hKey);
	if (!hKey)
	{
		return status;
	}
	RpcTryExcept
		return ::RootRpcRegRestoreKey(hKey, lpFile, dwFlags);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		return ::RpcExceptionCode();
	RpcEndExcept
}

ROOTRPC_EXPORTED LONG RootRegSaveKey(
	HKEY hKey,
	LPCWSTR lpFile,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
	LONG status;
	if (lpSecurityAttributes)
	{
		// We can't handle file security descriptors yet, and that's all this is used for.
		return ERROR_NOT_CAPABLE;
	}
	status = MapHKeyForRpc(&hKey);
	if (!hKey)
	{
		return status;
	}
	RpcTryExcept
		return ::RootRpcRegSaveKey(hKey, lpFile, nullptr);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		return ::RpcExceptionCode();
	RpcEndExcept
}

ROOTRPC_EXPORTED LONG RootRegSaveKeyEx(
	HKEY hKey,
	LPCWSTR lpFile,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD Flags)
{
	LONG status;
	if (lpSecurityAttributes)
	{
		// We can't handle file security descriptors yet, and that's all this is used for.
		return ERROR_NOT_CAPABLE;
	}
	status = MapHKeyForRpc(&hKey);
	if (!hKey)
	{
		return status;
	}
	RpcTryExcept
		return ::RootRpcRegSaveKeyEx(hKey, lpFile, nullptr, Flags);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		return ::RpcExceptionCode();
	RpcEndExcept
}

// Not actually wrapping a RootRpc function, just wrapping a native one with RootRpc help.
ROOTRPC_EXPORTED LONG RootRegSetKeyValue(
	HKEY hKey,
	LPCWSTR lpSubKey,
	LPCWSTR lpValueName,
	DWORD dwType,
	LPCVOID lpData,
	DWORD cbData)
{
	HKEY hk;
	LONG errval = ::RootRegOpenKeyEx(hKey, lpSubKey, 0x0, KEY_SET_VALUE, &hk);
	if (errval)
	{
		return errval;
	}
	errval = ::RegSetKeyValueW(hk, nullptr, lpValueName, dwType, lpData, cbData);
	::RegCloseKey(hk);
	return errval;
}

#endif

#ifndef NO_ROOTRPC_PROCESS_APIS
ROOTRPC_EXPORTED HANDLE RootCreateJobObject(
	LPSECURITY_ATTRIBUTES lpJobAttributes,
	LPCWSTR lpName)
{
	HANDLE ret = nullptr;
	WINERR err;
	ROOTRPC_SECURITY_ATTRIBUTES sa;
	if (lpJobAttributes)
	{
		sa.nLength = lpJobAttributes->nLength;
		sa.bInheritHandle = lpJobAttributes->bInheritHandle;
		sa.lpSecurityDescriptor = 
			reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(lpJobAttributes->lpSecurityDescriptor);
	}
	RpcTryExcept
		err = ::RootRpcCreateJobObject(
			lpJobAttributes ? &sa : nullptr,
			lpName,
			&ret);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (err)
	{
		::SetLastError(err);
	}
	return ret;
}

ROOTRPC_EXPORTED BOOL RootCreateProcess(
	LPCWSTR lpApplicationName,
	LPWSTR lpCommandLine,
	LPSECURITY_ATTRIBUTES lpProcessAttributes,
	LPSECURITY_ATTRIBUTES lpThreadAttributes,
	BOOL bInheritHandles,
	DWORD dwCreationFlags,
	LPVOID lpEnvironment,
	LPCWSTR lpCurrentDirectory,
	LPSTARTUPINFOW lpStartupInfo,
	LPPROCESS_INFORMATION lpProcessInformation)
{
	WINERR err;
	ROOTRPC_SECURITY_ATTRIBUTES psa, tsa;
	if (!lpProcessInformation)
	{
		::SetLastError(ERROR_BAD_ARGUMENTS);
		return FALSE;
	}
	if (bInheritHandles || (dwCreationFlags & EXTENDED_STARTUPINFO_PRESENT))
	{	// Unfortunately, can't do these yet
		// TODO: Add support for extended startup info
		::memset(lpProcessInformation, 0, sizeof(PROCESS_INFORMATION));
		::SetLastError(ERROR_NOT_CAPABLE);
		return FALSE;
	}
	if (lpProcessAttributes)
	{
		psa.nLength = lpProcessAttributes->nLength;
		psa.bInheritHandle = lpProcessAttributes->bInheritHandle;
		psa.lpSecurityDescriptor = reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
			lpProcessAttributes->lpSecurityDescriptor);
	}
	if (lpThreadAttributes)
	{
		tsa.nLength = lpThreadAttributes->nLength;
		tsa.bInheritHandle = lpThreadAttributes->bInheritHandle;
		tsa.lpSecurityDescriptor = reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
			lpThreadAttributes->lpSecurityDescriptor);
	}
	RpcTryExcept
		err = ::RootRpcCreateProcess(
			lpApplicationName,
			lpCommandLine,
			lpProcessAttributes ? &psa : nullptr,
			lpThreadAttributes ? &tsa : nullptr,
			bInheritHandles,
			dwCreationFlags,
			GetMultiStringSize(reinterpret_cast<PWCHAR>(lpEnvironment)),
			reinterpret_cast<PBYTE>(lpEnvironment),
			lpCurrentDirectory,
			lpStartupInfo,
			lpProcessInformation);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (err)
	{
		::SetLastError(err);
	}
	return !err;
}

ROOTRPC_EXPORTED BOOL RootCreateProcessAsUser(
	HANDLE hToken,
	LPCWSTR lpApplicationName,
	LPWSTR lpCommandLine,
	LPSECURITY_ATTRIBUTES lpProcessAttributes,
	LPSECURITY_ATTRIBUTES lpThreadAttributes,
	BOOL bInheritHandles,
	DWORD dwCreationFlags,
	LPVOID lpEnvironment,
	LPCWSTR lpCurrentDirectory,
	LPSTARTUPINFOW lpStartupInfo,
	LPPROCESS_INFORMATION lpProcessInformation)
{
	WINERR err;
	ROOTRPC_SECURITY_ATTRIBUTES psa, tsa;
	if (!lpProcessInformation)
	{
		::SetLastError(ERROR_BAD_ARGUMENTS);
		return FALSE;
	}
	if (bInheritHandles || (dwCreationFlags & EXTENDED_STARTUPINFO_PRESENT))
	{	// Unfortunately, can't do these yet
		// TODO: Add support for extended startup info
		::memset(lpProcessInformation, 0, sizeof(PROCESS_INFORMATION));
		::SetLastError(ERROR_NOT_CAPABLE);
		return FALSE;
	}
	if (lpProcessAttributes)
	{
		psa.nLength = lpProcessAttributes->nLength;
		psa.bInheritHandle = lpProcessAttributes->bInheritHandle;
		psa.lpSecurityDescriptor = reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
			lpProcessAttributes->lpSecurityDescriptor);
	}
	if (lpThreadAttributes)
	{
		tsa.nLength = lpThreadAttributes->nLength;
		tsa.bInheritHandle = lpThreadAttributes->bInheritHandle;
		tsa.lpSecurityDescriptor = reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
			lpThreadAttributes->lpSecurityDescriptor);
	}
	RpcTryExcept
		err = ::RootRpcCreateProcessAsUser(
			hToken,
			lpApplicationName,
			lpCommandLine,
			lpProcessAttributes ? &psa : nullptr,
			lpThreadAttributes ? &tsa : nullptr,
			bInheritHandles,
			dwCreationFlags,
			GetMultiStringSize(reinterpret_cast<PWCHAR>(lpEnvironment)),
			reinterpret_cast<PBYTE>(lpEnvironment),
			lpCurrentDirectory,
			lpStartupInfo,
			lpProcessInformation);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
		if (err)
		{
			::SetLastError(err);
		}
	return !err;
}

ROOTRPC_EXPORTED BOOL RootEnumProcesses(
	DWORD *pProcessIds,
	DWORD cb,
	DWORD *pBytesReturned)
{
	WINERR err;
	RpcTryExcept
		err = ::RootRpcEnumProcesses(cb, pProcessIds, pBytesReturned);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (err)
	{
		::SetLastError(err);
	}
	return !err;
}

ROOTRPC_EXPORTED HANDLE RootOpenJobObject(
	DWORD dwDesiredAccess,
	BOOL bInheritHandle,
	LPWSTR lpName)
{
	WINERR err;
	HANDLE ret = nullptr;
	RpcTryExcept
		err = ::RootRpcOpenJobObject(dwDesiredAccess, bInheritHandle, lpName, &ret);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (!ret)
	{
		::SetLastError(err);
	}
	return ret;
}

ROOTRPC_EXPORTED HANDLE RootOpenProcess(
	DWORD dwDesiredAccess,
	BOOL bInheritHandle,
	DWORD dwProcessId)
{
	HANDLE proc = nullptr;
	WINERR err;
	RpcTryExcept
		err = ::RootRpcOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId, &proc);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (err)
	{
		::SetLastError(err);
	}
	// proc will be null if there was an error, just as it should be.
	return proc;
}

ROOTRPC_EXPORTED HANDLE RootOpenThread(
	DWORD dwDesiredAccess,
	BOOL bInheritHandle,
	DWORD dwThreadId)
{
	HANDLE proc = nullptr;
	WINERR err;
	RpcTryExcept
		err = ::RootRpcOpenThread(dwDesiredAccess, bInheritHandle, dwThreadId, &proc);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (err)
	{
		::SetLastError(err);
	}
	// proc will be null if there was an error, just as it should be.
	return proc;
}
#endif

#ifndef NO_ROOTRPC_PIPE_APIS
ROOTRPC_EXPORTED BOOL RootCallNamedPipe(
	LPCWSTR lpNamedPipeName,
	LPVOID lpInBuffer,
	DWORD nInBufferSize,
	LPVOID lpOutBuffer,
	DWORD nOutBufferSize,
	LPDWORD lpBytesRead,
	DWORD nTimeOut)
{
	WINERR err;
	RpcTryExcept
		err = ::RootRpcCallNamedPipe(
			lpNamedPipeName,
			nInBufferSize,
			reinterpret_cast<LPBYTE>(lpInBuffer),
			nOutBufferSize,
			reinterpret_cast<LPBYTE>(lpOutBuffer),
			lpBytesRead,
			nTimeOut);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (err)
	{
		::SetLastError(err);
	}
	return !err;
}

ROOTRPC_EXPORTED HANDLE RootCreateNamedPipe(
	LPCWSTR lpName,
	DWORD dwOpenMode,
	DWORD dwPipeNode,
	DWORD nMaxInstances,
	DWORD nOutBufferSize,
	DWORD nInBufferSize,
	DWORD nDefaultTimeout,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
	WINERR err;
	HANDLE ret = INVALID_HANDLE_VALUE;
	ROOTRPC_SECURITY_ATTRIBUTES sa = { 0 };
	if (lpSecurityAttributes)
	{
		sa.nLength = lpSecurityAttributes->nLength;
		sa.bInheritHandle = lpSecurityAttributes->bInheritHandle;
		sa.lpSecurityDescriptor = (lpSecurityAttributes->lpSecurityDescriptor ?
			reinterpret_cast<ROOTRPC_PSECURITY_DESCRIPTOR>(
				lpSecurityAttributes->lpSecurityDescriptor) :
			nullptr);
	}
	RpcTryExcept
		err = ::RootRpcCreateNamedPipe(
			lpName,
			dwOpenMode,
			dwPipeNode,
			nMaxInstances,
			nOutBufferSize,
			nInBufferSize,
			nDefaultTimeout,
			(lpSecurityAttributes ? &sa : nullptr),
			&ret);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (INVALID_HANDLE_VALUE == ret)
	{
		::SetLastError(err);
	}
	return ret;
}
ROOTRPC_EXPORTED BOOL RootWaitNamedPipe(
	LPCWSTR lpNamedPipeName,
	DWORD nTimeOut)
{
	WINERR err;
	RpcTryExcept
		err = ::RootRpcWaitNamedPipe(
			lpNamedPipeName,
			nTimeOut);
	RpcExcept(EXCEPTION_EXECUTE_HANDLER)
		err = ::RpcExceptionCode();
	RpcEndExcept
	if (err)
	{
		::SetLastError(err);
	}
	return !err;
}
#endif
